如第4章和第9章所述，工作项是并行层次结构的叶节点，并表示内核函数的单个实例。工作项可以以任何顺序执行，并且不能相互通信或同步，除非通过对局部和全局内存的原子内存操作，或者通过组集合函数(例如shuffle、barrier)。\par

正如本章开始所描述的，DPC++中的向量是为了方便使用，每个向量对于单个工作项都是本地的(而不是与硬件中的向量化相关)，因此可以看作是工作项中的numElements的私有数组。例如，“float4 y4”声明的存储等价于float y4[4]。如图11-8所示。\par

\hspace*{\fill} \par %插入空行
图11-8 向量执行示例
\begin{lstlisting}[caption={}]
Q.prallel_for(8, [=](id<1> i){
	...
	float  x  = a[i]; // i = 1,2,3...,7
	float4 y4 = b[i]; // i = 1,2,3...,7
	...
});
\end{lstlisting}

对于标量变量x，在具有SIMD指令(例如，CPU、GPU)的硬件上使用多个工作项的内核执行可能会使用向量寄存器和SIMD指令，但向量化是跨工作项的，并且与代码中的任何向量类型无关。每个工作项可以在vec\_x中的不同位置上操作，如图11-9所示。工作项中的标量数据，可以看作在工作项之间的隐式向量化(合并到SIMD硬件指令中)，但编写的工作项代码没有对此进行编码——这是SPMD编程风格的核心。

\hspace*{\fill} \par %插入空行
图11-9 从标量变量x到vec\_x[8]的向量展开
\begin{table}[H]
	\begin{tabular}{|l|l|l|l|l|l|l|l|l|}
		\hline
		work-item ID & w0 & w1 & w2 & w3 & w4 & w5 & w6 & w7 \\ \hline
		vec\_x       & a0 & a1 & a2 & a3 & a4 & a5 & a6 & a7 \\ \hline
	\end{tabular}
\end{table}

通过编译器标量变量x到vec\_x[8]的隐式向量展开(如图11-9所示)，编译器从出现在多个工作项中的标量操作创建一个SIMD操作。\par

对于向量变量y4，内核执行多个工作项(例如：8个工作项)的结果不会通过在硬件中使用向量操作来处理vec4。每个工作项独立地使用向量，并且该向量上元素的操作发生在多个时钟周期/指令上(编译器扩展了向量)，如图11-10所示。\par

\hspace*{\fill} \par %插入空行
图11-10 垂直展开到y4的vec\_y[8][4]的等量，横跨8个工作项
\begin{table}[]
	\begin{tabular}{|l|l|l|l|l|}
		\hline
		work-item ID & vec\_y0 & vec\_y1 & vec\_y2 & vec\_y3 \\ \hline
		w0           & b0      & b0      & b0      & b0      \\ \hline
		w1           & b1      & b1      & b1      & b1      \\ \hline
		w2           & b2      & b2      & b2      & b2      \\ \hline
		w3           & b3      & b3      & b3      & b3      \\ \hline
		w4           & b4      & b4      & b4      & b4      \\ \hline
		w5           & b5      & b5      & b5      & b5      \\ \hline
		w6           & b6      & b6      & b6      & b6      \\ \hline
		w7           & b7      & b7      & b7      & b7      \\ \hline
	\end{tabular}
\end{table}

每个工作项都看到y4的原始数据布局，这提供了一个直观的模型来进行推理和调优。性能的缺点是编译器必须为CPU和GPU生成收集/分散内存指令，如图11-11所示(向量在内存中是连续的，并且相邻的工作项并行地在不同的向量上操作)，因此，当编译器将跨工作项(例如：跨子组)向量化时，标量通常是显式向量的有效方法。详见第15章和第16章。\par

\hspace*{\fill} \par %插入空行
图11-11 带有地址转义的向量代码示例
\begin{lstlisting}[caption={}]
Q.prallel_for(8, [=](id<1> i){
	...
	float  x  = a[i]; // i = 1,2,3...,7

	//"dowork" expects y4 with vec_y[8][4] data layout
	float x = dowork(&y4);
	...
});
\end{lstlisting}

当编译器能够证明y4的地址没有从当前内核工作项中转移，或者所有调用的函数都将内联，那么编译器可能会执行优化，就像使用一组向量寄存器从y4水平展开到vec\_y[4][8]一样，如图11-12所示。这种情况下，编译器可以在不收集/分布SIMD指令的情况下获得最佳性能。编译器优化报告向程序员提供了关于这种类型转换的信息，可以提供关于如何调整代码以提高性能的提示。\par

\hspace*{\fill} \par %插入空行
图11-12 将y4扩展到vec\_y[4][8]
\begin{table}[H]
	\begin{tabular}{|l|l|l|l|l|l|l|l|l|}
		\hline
		work-item ID & w0 & w1 & w2 & w3 & w4 & w5 & w6 & w7 \\ \hline
		y0           & b0 & b1 & b2 & b3 & b4 & b5 & b6 & b7 \\ \hline
		y1           & b0 & b1 & b2 & b3 & b4 & b5 & b6 & b7 \\ \hline
		y2           & b0 & b1 & b2 & b3 & b4 & b5 & b6 & b7 \\ \hline
		y3           & b0 & b1 & b2 & b3 & b4 & b5 & b6 & b7 \\ \hline
	\end{tabular}
\end{table}















